[Feature] Native Slack task cards for Fast delegations - #1431
Conversation
|
No new code issues found. 1 issue outstanding. See task
Reviewed d89a67c |
| sentStatuses.set(todo.id, rowStatus); | ||
| return true; | ||
| }); | ||
|
|
There was a problem hiding this comment.
sentStatuses is updated before appendTaskStream has succeeded. If Slack returns false (or the append throws and the callback catches it), this update permanently records all emitted todo rows as delivered; the next identical todo_update filters them out, so the card never receives its checklist entries. Commit these statuses only after a successful append, or roll them back on failure, and cover the false-return path.
There was a problem hiding this comment.
Resolved by the single-entry card refactor: it no longer records per-todo delivery state before calling Slack.
There was a problem hiding this comment.
This is reintroduced by the new delta guard: slackLiveTaskLastDetails is set before appendTaskStream has succeeded, while the notifier reports a rejected Slack response as false. A failed append therefore suppresses a later event with the same detail, leaving the card stale. Record the line only after a successful append (or roll it back on failure), and cover the false path.
There was a problem hiding this comment.
This remains unresolved in the title/output refactor: slackLiveTaskCardTitle and slackLiveTaskLastOutput are recorded before appendTaskStream resolves, and its false result is ignored. A rejected append makes an identical subsequent step or narrative update short-circuit at the delta guard, leaving the card stale. Commit those values only after a successful append and cover the false path.
There was a problem hiding this comment.
Still unresolved. appendTaskStream can return false, but slackLiveTaskCardTitle/slackLiveTaskLastOutput are committed before that call (lines 181-185). The new narration throttle also sets slackLiveTaskStepNarrated before awaiting the append (line 276), so one rejected append suppresses every later narration until a todo update. Commit or restore all delivery guards only after a successful append, and cover the false path.
Fast-delegated tasks now post a live task card into the parent thread alongside the kickoff, using Slack's native agent task streams (chat.startStream/appendStream/stopStream with task_update chunks). - The launcher opens the stream in beforeEnqueue: entry title = task title, View task source link (sent once; Slack appends sources per chunk), plus an immediate repaint append (the client does not render a stream whose only content is the opening chunk) - The worker projects callback events onto the card through run-scoped SDK endpoints (sandboxed workers cannot reach control-plane Redis): the entry title carries the current todo with progress (title/status replace on append; details/output accumulate), narrative text streams into the output body as newline-prefixed deltas, and request_user_input shows a waiting state - Card state is keyed by task id and the liveTaskStream flag is inherited across snapshot resumes, so resumed runs keep updating the same card; the card-data endpoint overlays the task's generated title - Card callbacks are wired by the payload flag for every payload kind, with an onExit fallback that settles the card (complete or error with output) even when the completion event is lost - task_update fields are size-capped and Slack response warnings logged, so dropped chunks are visible
6af7ec7 to
2247394
Compare
Replace the append-only streaming approach with the task_card Block Kit block in an ordinary message, re-rendered wholesale via chat.update on every callback event. The card body always shows just the latest state (current todo with progress, latest narration line), transient states vanish on the next render instead of accumulating, and completion swaps in the full output. Removes the now-unused stream methods from the notifier.
…in place" This reverts commit d5bfb40.
Back on the streaming card (chat.update re-renders reset the client's expand state; stream appends preserve it): - task_display_mode 'dense' collapses consecutive tool-call entries - Transient status lines (provider retries) never enter the permanent body - Each step contributes at most one narration line, keeping long runs readable
The generated title usually lands right after enqueue; the launcher now re-titles the card immediately (with one short retry) instead of leaving the prompt-derived title until settle. Bounded to the pre-worker window so step titles are never overwritten.
The API rejects 'dense' as an invalid enum value (documented but not accepted), which failed chat.startStream and dropped the card entirely.
Re-home the Fast live task-card launch onto develop's cloud-agents launcher: generic afterKickoff/afterLaunch/liveTaskStream hooks on createFastAgentTaskLauncher, and a Slack-specific createFastAgentSlackLiveTaskLauncher in @roomote/slack used by both the api message entry and the sdk parent-event path.
The native task card is the progress surface for Fast turns, so the automatic eyes reaction (and the isExistingConversation gating that only existed to suppress it) is gone from the Slack Fast path.
- Title is the only task_update field Slack replaces, so the card now shows just the latest progress line there (current todo without a step count, latest narration, or waiting state) and writes nothing to the body until it settles. - The worker pushes the generated task title at start (and the launcher polls longer for it) so the prompt-derived opening title gets replaced. - A failed turn no longer settles the card as an error and wipes its stream data: the workspace is retained for resume, so the next run of the task keeps driving the same card and flips it back to in progress. - When Slack reports the stream is gone (expired server-side or already stopped), the card falls back to chat.update on the same message.
Streamed task_update chunks only replace title/status; details, output and sources accumulate, which is why the body jammed messages together. Post the card as a task_card block in an ordinary message instead and re-render the whole block through chat.update on every change, so the card shows the task title, the current step, and only the latest agent message. This also removes the stream lifetime problem (no stream to expire) and the recipient plumbing that only chat.startStream needed.
Slack generates a new block_id on every chat.update when none is given, which remounts the task_card in the client and collapses it on each render. Pin it to the task so every update is the same block.
Fast-delegated tasks now stream a native Slack agent task card into the parent thread, showing live progress alongside the kickoff.
What this does
chat.startStreamwithtask_updatechunks) inbeforeEnqueue: the entry title is the task title, with a View task source link and an immediate repaint append (the Slack client does not render a stream whose only content is its opening chunk).title/statusreplace on append whiledetails/output/sourcesaccumulate — so the entry title carries the current todo with progress (swapping cleanly per step), narrative streams into the output body as newline-prefixed deltas, and the source link is sent exactly once.liveTaskStreampayload flag is inherited across snapshot resumes, so resumed runs keep updating the same card; the card-data endpoint overlays the task's generated title.onExitfallback that settles the card (complete, or error with output for cancel/failure) even when the completion event is lost.task_updatefields are size-capped and Slack response warnings are logged, so silently dropped chunks are visible.Open items